package java_thinking.unit_21._21_5;

import java.util.Map;
import java.util.concurrent.*;

/**
 * @Desc:   5-19 最终可靠且高并发性能的 缓存实现
 *          当缓存的是Future而不是值时， 将导致缓存污染(Cache Pollution)问题： 如果某个计算被
 *          取消或者失败，那么在计算这个结果时将指明计算过程被取消或者失败。为了避免 这种情况，
 *          如果 Memoizer 发现计算被取消，那么将把 Future 从缓存中移除。如果检测到
 *          RuntimeException, 那么也会移除 Future, 这样将来的计算才可能成功。Memoizer 同样没
 *          有解决缓存逾期的问题，但它可以通过使用 FutureTask 的子类来解决，在子类中为每个结果
 *          指定一 个逾期时间，并定期扫描缓存中逾期的元素。(同样，它也没有解决缓存清理的问题，
 *          即移除旧的计算结果以便为新的计算结果腾出空间，从而使缓存不会消耗过多的内存。)
 *          缓存逾期等问题 ：https://www.cnblogs.com/lingshao/p/5658757.html
 *
 * @author: cww
 * @DateTime: 2019/6/10 19:45
 */

public class Memoizer<A, V> implements Computable<A, V>{
    private final Map<A, Future<V>> cache = new ConcurrentHashMap<>();
    private final Computable<A, V> c;

    public Memoizer(Computable<A, V> c) {
        this.c = c;
    }

    @Override
    public V compute(A arg) throws InterruptedException {
        /**
         * 为什么要while (true)
         * 这是因为future的get方法会由于线程被中断而抛出CancellationException。
         * 我们对于CancellationException的处理是cache.remove(arg, f);，也就是把缓存清理掉，然后进入下一个循环，重新计算，
         * 直到计算成功，或者抛出ExecutionException。
         *
         */
        while (true) {
            Future<V> f = cache.get(arg);
            if (f == null) {
                Callable<V> eval = () -> c.compute(arg);
                FutureTask<V> ft = new FutureTask<V>(eval);
                // 使用putIfAbsent测试是否真的将ft存入了缓存, 如果存入失败, 说明cache中已经存在arg对应的future对象
                // 否则才进行计算.
                f = cache.putIfAbsent(arg,ft);
                if (f == null) {
                    f = ft;
                    ft.run();
                }
                try {
                    return f.get();
                } catch (CancellationException e) {
                    // 当计算被取消时, 从缓存中移除arg-f键值对
                    cache.remove(arg, f);
                } catch (ExecutionException e) {
                    throw LaunderThrowable.launderThrowable(e.getCause());
                }
            }
        }
    }
}
